/*
	Written By Pradipna Nepal
	www.pradsprojects.com

	Copyright (C) 2011 Prads Projects
	Please read COPYING.txt included along with this source code for more detail.
	If not included, see http://www.gnu.org/licenses/
*/

#include "libDebugger.h"

bool startDebug = false;
int disassemblySize = 0;

void createControls(HWND hwnd);
void updateRegsLabels();
void disassembleToLB();
void trace();
void editRegisters(int cbIndex);
void editMemory(int cbIndex);
int hexToInt(char *hex, int size);

BOOL CALLBACK Debugger::debugWndProc(HWND hwnd, UINT msG, WPARAM wParam, LPARAM lParam) {
	switch (msG) {
		case WM_INITDIALOG:
			createControls(hwnd);
			break;
		case WM_COMMAND:
			switch (LOWORD(wParam)) {
				case IDC_DBG_BTN_TRACE:
					trace();
					break;
				case IDC_DBG_BTN_CLEAR:
					SendDlgItemMessage(hwnd, IDC_DBG_LB_TRACE, CB_RESETCONTENT, 0, 0);
					break;
				case IDC_DBG_BTN_REGISTERS:
					{
						int cbIndex = SendDlgItemMessage(hwnd, IDC_DBG_CB_REGISTERS, CB_GETCURSEL, 0, 0);
						editRegisters(cbIndex);
					}
					break;
				case IDC_DBG_BTN_MEMORY:
					{
						int cbIndex = SendDlgItemMessage(hwnd, IDC_DBG_CB_MEMORY, CB_GETCURSEL, 0, 0);
						editMemory(cbIndex);
					}
					break;
				case IDC_DBG_CB_MEMORY:
					if (HIWORD(wParam) == CBN_SELCHANGE) {
						int cbIndex = SendDlgItemMessage(hwnd, IDC_DBG_CB_MEMORY, CB_GETCURSEL, 0, 0);
						int memVal = (int)cpu->getMemory()[cbIndex];

						SetDlgItemText(hwnd, IDC_DBG_TXT_MEMORY, intToHexNum(memVal).c_str());
					}
					break;
				case DEBUGGER_COMMAND_INITIALIZE:
					initDebugger();
			}
			break;
		case WM_KEYDOWN:
			if (LOWORD(wParam) == 'T') trace();
			break;
		case WM_CLOSE:
			startDebug = false;
			ShowWindow(hwnd, SW_HIDE);
			break;
		default:
			return FALSE;
	}
	return TRUE;
}

void Debugger::initDebugger() {
	//Delete everything in ListBoxes
	SendDlgItemMessage(hwndDebug, IDC_DBG_LB_DISASSEMBLY, LB_RESETCONTENT, 0, 0);
	SendDlgItemMessage(hwndDebug, IDC_DBG_LB_TRACE, LB_RESETCONTENT, 0, 0);

	//Update ComboBoxes Selection
	SendDlgItemMessage(hwndDebug, IDC_DBG_CB_REGISTERS, CB_SETCURSEL, 0, 0);
	SendDlgItemMessage(hwndDebug, IDC_DBG_CB_MEMORY, CB_SETCURSEL, 0, 0);

	disassembleToLB();
	updateRegsLabels();
}

void createControls(HWND hwnd) {
	//Labels
	CreateWindow("STATIC", "abcd", WS_VISIBLE | WS_CHILD, 60, 33, 50, 20, hwnd, 
		(HMENU)IDC_DBG_LBL_PC, GetModuleHandle(NULL), 0);

	CreateWindow("STATIC", "ffff", WS_VISIBLE | WS_CHILD, 60, 57, 50, 20, hwnd, 
		(HMENU)IDC_DBG_LBL_SP, GetModuleHandle(NULL), 0);

	CreateWindow("STATIC", "ffff", WS_VISIBLE | WS_CHILD, 60, 81, 50, 20, hwnd, 
		(HMENU)IDC_DBG_LBL_FLAG, GetModuleHandle(NULL), 0);

	CreateWindow("STATIC", "ffff", WS_VISIBLE | WS_CHILD, 60, 106, 50, 20, hwnd, 
		(HMENU)IDC_DBG_LBL_R_ZERO, GetModuleHandle(NULL), 0);

	CreateWindow("STATIC", "ffff", WS_VISIBLE | WS_CHILD, 60, 130, 50, 20, hwnd, 
		(HMENU)IDC_DBG_LBL_R_ONE, GetModuleHandle(NULL), 0);

	CreateWindow("STATIC", "ffff", WS_VISIBLE | WS_CHILD, 60, 154, 50, 20, hwnd, 
		(HMENU)IDC_DBG_LBL_R_TWO, GetModuleHandle(NULL), 0);
	
	CreateWindow("STATIC", "ffff", WS_VISIBLE | WS_CHILD, 60, 179, 50, 20, hwnd, 
		(HMENU)IDC_DBG_LBL_R_THREE, GetModuleHandle(NULL), 0);

	CreateWindow("STATIC", "ffff", WS_VISIBLE | WS_CHILD, 60, 203, 50, 20, hwnd, 
		(HMENU)IDC_DBG_LBL_R_FOUR, GetModuleHandle(NULL), 0);

	CreateWindow("STATIC", "ffff", WS_VISIBLE | WS_CHILD, 60, 228, 50, 20, hwnd, 
		(HMENU)IDC_DBG_LBL_R_FIVE, GetModuleHandle(NULL), 0);

	CreateWindow("STATIC", "ffff", WS_VISIBLE | WS_CHILD, 60, 252, 50, 20, hwnd, 
		(HMENU)IDC_DBG_LBL_R_SIX, GetModuleHandle(NULL), 0);

	CreateWindow("STATIC", "ffff", WS_VISIBLE | WS_CHILD, 60, 276, 50, 20, hwnd, 
		(HMENU)IDC_DBG_LBL_R_SEVEN, GetModuleHandle(NULL), 0);

	CreateWindow("STATIC", "ffff", WS_VISIBLE | WS_CHILD, 60, 301, 50, 20, hwnd, 
		(HMENU)IDC_DBG_LBL_R_EIGHT, GetModuleHandle(NULL), 0);

	CreateWindow("STATIC", "ffff", WS_VISIBLE | WS_CHILD, 60, 325, 50, 20, hwnd, 
		(HMENU)IDC_DBG_LBL_R_NINE, GetModuleHandle(NULL), 0);

	CreateWindow("STATIC", "ffff", WS_VISIBLE | WS_CHILD, 60, 349, 50, 20, hwnd, 
		(HMENU)IDC_DBG_LBL_R_A, GetModuleHandle(NULL), 0);

	CreateWindow("STATIC", "ffff", WS_VISIBLE | WS_CHILD, 60, 373, 50, 20, hwnd, 
		(HMENU)IDC_DBG_LBL_R_B, GetModuleHandle(NULL), 0);

	CreateWindow("STATIC", "ffff", WS_VISIBLE | WS_CHILD, 60, 398, 50, 20, hwnd, 
		(HMENU)IDC_DBG_LBL_R_C, GetModuleHandle(NULL), 0);
	
	CreateWindow("STATIC", "ffff", WS_VISIBLE | WS_CHILD, 60, 423, 50, 20, hwnd, 
		(HMENU)IDC_DBG_LBL_R_D, GetModuleHandle(NULL), 0);
	
	CreateWindow("STATIC", "ffff", WS_VISIBLE | WS_CHILD, 60, 447, 50, 20, hwnd, 
		(HMENU)IDC_DBG_LBL_R_E, GetModuleHandle(NULL), 0);

	CreateWindow("STATIC", "ffff", WS_VISIBLE | WS_CHILD, 60, 471, 50, 20, hwnd, 
		(HMENU)IDC_DBG_LBL_R_F, GetModuleHandle(NULL), 0);

	CreateWindow("STATIC", "ffff", WS_VISIBLE | WS_CHILD, 160, 466, 40, 20, hwnd, 
		(HMENU)IDC_DBG_LBL_C_ONE, GetModuleHandle(NULL), 0);

	CreateWindow("STATIC", "ffff", WS_VISIBLE | WS_CHILD, 245, 466, 40, 20, hwnd, 
		(HMENU)IDC_DBG_LBL_C_TWO, GetModuleHandle(NULL), 0);

	CreateWindow("STATIC", "1234", WS_VISIBLE | WS_CHILD, 345, 466, 40, 20, hwnd, 
		(HMENU)IDC_DBG_LBL_CYCLE, GetModuleHandle(NULL), 0);

	//Combo Boxes
	CreateWindow("COMBOBOX", NULL, WS_VISIBLE | WS_CHILD | WS_VSCROLL | CBS_DROPDOWNLIST, 
		131, 28, 100, 150, hwnd, (HMENU)IDC_DBG_CB_REGISTERS, GetModuleHandle(NULL), 0);

	CreateWindow("COMBOBOX", NULL, WS_VISIBLE | WS_CHILD | WS_VSCROLL | CBS_DROPDOWNLIST, 
		418, 28, 100, 150, hwnd, (HMENU)IDC_DBG_CB_MEMORY, GetModuleHandle(NULL), 0);

	//Text Boxes
	CreateWindow("EDIT", NULL, WS_VISIBLE | WS_CHILD | WS_BORDER | ES_RIGHT, 
		243, 28, 70, 20, hwnd, (HMENU)IDC_DBG_TXT_REGISTERS, GetModuleHandle(NULL), 0);

	CreateWindow("EDIT", NULL, WS_VISIBLE | WS_CHILD | WS_BORDER | ES_RIGHT, 
		530, 28, 70, 20, hwnd, (HMENU)IDC_DBG_TXT_MEMORY, GetModuleHandle(NULL), 0);
	
	//Buttons
	CreateWindow("BUTTON", "Edit", WS_VISIBLE | WS_CHILD, 
		325, 26, 70, 23, hwnd, (HMENU)IDC_DBG_BTN_REGISTERS, GetModuleHandle(NULL), 0);

	CreateWindow("BUTTON", "Edit", WS_VISIBLE | WS_CHILD, 
		612, 26, 70, 23, hwnd, (HMENU)IDC_DBG_BTN_MEMORY, GetModuleHandle(NULL), 0);

	CreateWindow("BUTTON", "Trace (T)", WS_VISIBLE | WS_CHILD, 
		603, 460, 80, 25, hwnd, (HMENU)IDC_DBG_BTN_TRACE, GetModuleHandle(NULL), 0);

	CreateWindow("BUTTON", "Clear", WS_VISIBLE | WS_CHILD, 
		517, 460, 80, 25, hwnd, (HMENU)IDC_DBG_BTN_CLEAR, GetModuleHandle(NULL), 0);

	//List Box
	CreateWindow("LISTBOX", "Edit", WS_VISIBLE | WS_CHILD | WS_BORDER | WS_VSCROLL | WS_HSCROLL, 
		131, 75, 271, 355, hwnd, (HMENU)IDC_DBG_LB_DISASSEMBLY, GetModuleHandle(NULL), 0);

	CreateWindow("LISTBOX", "Edit", WS_VISIBLE | WS_CHILD | WS_BORDER | WS_VSCROLL | WS_HSCROLL, 
		410, 75, 271, 355, hwnd, (HMENU)IDC_DBG_LB_TRACE, GetModuleHandle(NULL), 0);

	//Set Font for Controls
	HFONT controlsFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
	for (int i = 101; i < 133; ++i) SendDlgItemMessage(hwnd, i, WM_SETFONT, (WPARAM)controlsFont, TRUE);

	//Delete Font
	DeleteObject(controlsFont);

	//Populate ComboBoxes
	for (int i = 0; i < 0xFFFF; ++i)
		SendDlgItemMessage(hwnd, IDC_DBG_CB_MEMORY, CB_ADDSTRING, 0, (LPARAM)intToHexNum(i).c_str());
	SendDlgItemMessage(hwnd, IDC_DBG_CB_REGISTERS, CB_ADDSTRING, 0, (LPARAM)"PC");
	SendDlgItemMessage(hwnd, IDC_DBG_CB_REGISTERS, CB_ADDSTRING, 0, (LPARAM)"SP");
	SendDlgItemMessage(hwnd, IDC_DBG_CB_REGISTERS, CB_ADDSTRING, 0, (LPARAM)"Flag");
	
	stringstream ss;
	for (int i = 0; i < 16; ++i) {
		ss.str(string());
		ss << "R" << i;
		SendDlgItemMessage(hwnd, IDC_DBG_CB_REGISTERS, CB_ADDSTRING, 0, (LPARAM)ss.str().c_str());
	}
}

void updateRegsLabels() {
	SetDlgItemText(hwndDebug, IDC_DBG_LBL_PC, intToHexNum(cpu->getPC()).c_str());		//PC
	SetDlgItemText(hwndDebug, IDC_DBG_LBL_SP, intToHexNum(cpu->getSP()).c_str());		//SP
	SetDlgItemText(hwndDebug, IDC_DBG_LBL_FLAG, intToHexNum(cpu->getFlag()).c_str());	//Flag
	SetDlgItemText(hwndDebug, IDC_DBG_LBL_R_ZERO, intToHexNum(cpu->getR(0)).c_str());	//R0
	SetDlgItemText(hwndDebug, IDC_DBG_LBL_R_ONE, intToHexNum(cpu->getR(1)).c_str());	//R1
	SetDlgItemText(hwndDebug, IDC_DBG_LBL_R_TWO, intToHexNum(cpu->getR(2)).c_str());	//R2
	SetDlgItemText(hwndDebug, IDC_DBG_LBL_R_THREE, intToHexNum(cpu->getR(3)).c_str());	//R3
	SetDlgItemText(hwndDebug, IDC_DBG_LBL_R_FOUR, intToHexNum(cpu->getR(4)).c_str());	//R4
	SetDlgItemText(hwndDebug, IDC_DBG_LBL_R_FIVE, intToHexNum(cpu->getR(5)).c_str());	//R5
	SetDlgItemText(hwndDebug, IDC_DBG_LBL_R_SIX, intToHexNum(cpu->getR(6)).c_str());	//R6
	SetDlgItemText(hwndDebug, IDC_DBG_LBL_R_SEVEN, intToHexNum(cpu->getR(7)).c_str());	//R7
	SetDlgItemText(hwndDebug, IDC_DBG_LBL_R_EIGHT, intToHexNum(cpu->getR(8)).c_str());	//R8
	SetDlgItemText(hwndDebug, IDC_DBG_LBL_R_NINE, intToHexNum(cpu->getR(9)).c_str());	//R9
	SetDlgItemText(hwndDebug, IDC_DBG_LBL_R_A, intToHexNum(cpu->getR(10)).c_str());		//R10
	SetDlgItemText(hwndDebug, IDC_DBG_LBL_R_B, intToHexNum(cpu->getR(11)).c_str());		//R11
	SetDlgItemText(hwndDebug, IDC_DBG_LBL_R_C, intToHexNum(cpu->getR(12)).c_str());		//R12
	SetDlgItemText(hwndDebug, IDC_DBG_LBL_R_D, intToHexNum(cpu->getR(13)).c_str());		//R13
	SetDlgItemText(hwndDebug, IDC_DBG_LBL_R_E, intToHexNum(cpu->getR(14)).c_str());		//R14
	SetDlgItemText(hwndDebug, IDC_DBG_LBL_R_F, intToHexNum(cpu->getR(15)).c_str());		//R15
	
	int controllers;
	unsigned char *cMem = cpu->getMemory();

	//C1
	controllers = (cMem[0xFFF1] << 8) | cMem[0xFFF0];
	SetDlgItemText(hwndDebug, IDC_DBG_LBL_C_ONE, intToHexNum(controllers).c_str());
	
	//C2
	controllers = (cMem[0xFFF3] << 8) | cMem[0xFFF2];
	SetDlgItemText(hwndDebug, IDC_DBG_LBL_C_TWO, intToHexNum(controllers).c_str());

	SetDlgItemInt(hwndDebug, IDC_DBG_LBL_CYCLE, cpu->getCycle(), FALSE);	//Cycle
}

void disassembleToLB() {
	unsigned char *cMem = cpu->getMemory();
	string assembly;

	//Disassemble the ROM and put it in ListBox
	for (int i = 0; i < romSize; i += 4) {
		assembly = string();
		assembly = intToHexNum(i) + "     " + disassemble(cMem, i);
		SendDlgItemMessage(hwndDebug, IDC_DBG_LB_DISASSEMBLY, LB_ADDSTRING, 0, (LPARAM)assembly.c_str());
	}

	disassemblySize = SendDlgItemMessage(hwndDebug, IDC_DBG_LB_DISASSEMBLY, LB_GETCOUNT, 0, 0);
}

void trace() {
	string assembly;

	//Put assembly in trace ListBox
	assembly = intToHexNum(cpu->getPC()) + "     " + disassemble(cpu->getMemory(), cpu->getPC());
	SendDlgItemMessage(hwndDebug, IDC_DBG_LB_TRACE, LB_ADDSTRING, 0,	(LPARAM)assembly.c_str());
	int lbItemSize = SendDlgItemMessage(hwndDebug, IDC_DBG_LB_TRACE, LB_GETCOUNT, 0, 0);
	SendDlgItemMessage(hwndDebug, IDC_DBG_LB_TRACE, LB_SETCURSEL, (WPARAM)(lbItemSize - 1), 0);
	
	cpu->fetchDecodeExecute();	//Run a cycle
	updateRegsLabels();		//Update Register Labels

	//If PC is pointing in ROM, select the assembly in ListBox
	int assemblyIndex = cpu->getPC() >> 2;
	if (assemblyIndex < disassemblySize) 
		SendDlgItemMessage(hwndDebug, IDC_DBG_LB_DISASSEMBLY, LB_SETCURSEL, (WPARAM)assemblyIndex, 0);
	
	SetFocus(hwndDebug);
}

//Text from TextBox to Memory
#define txtToMemory(buff, size, ctrlIndex) {		\
	size = GetWindowTextLength(GetDlgItem(hwndDebug, ctrlIndex));	\
	if (size == 0) return;	\
	buff = new (nothrow) char [size + 1];	\
	if (buff == 0) {	\
		MessageBox(hwndDebug, "Memory Allocation Error", "Error", MB_ICONERROR);	\
		return;	\
	}	\
	GetDlgItemText(hwndDebug, ctrlIndex, buff, size + 1);	\
}

void editRegisters(int cbIndex) {
	char *cBuffer = 0;
	int cSize;

	txtToMemory(cBuffer, cSize, IDC_DBG_TXT_REGISTERS);

	//Hex to Integer
	int intVal = hexToInt(cBuffer, cSize);
	if (intVal == -1) {
		MessageBox(hwndDebug, "Illegal character in HEX value", "Error", MB_ICONERROR);
		DEL_PTR(cBuffer);
		return;
	}

	//Put the value in register
	switch (cbIndex) {
		case 0:
			cpu->setPC(intVal);
			break;
		case 1:
			cpu->setSP(intVal);
			break;
		case 2:
			cpu->setFlag(intVal);
			break;
		default:
			cpu->setR(intVal, cbIndex - 3);
	}
	
	DEL_PTR(cBuffer);
	updateRegsLabels();
}

void editMemory(int cbIndex) {
	char *cBuffer;
	int cSize;

	txtToMemory(cBuffer, cSize, IDC_DBG_TXT_MEMORY);

	//Hex to Integer
	int intVal = hexToInt(cBuffer, cSize);
	if (intVal == -1) {
		MessageBox(hwndDebug, "Illegal character in HEX value", "Error", MB_ICONERROR);
		DEL_PTR(cBuffer);
		return;
	}

	cpu->setMemory(intVal, cbIndex);
	DEL_PTR(cBuffer);
}

int hexToInt(char *hex, int size) {
	int retVal = 0, nibble;

	for (int i = 0; i < size; ++i) {
		if (hex[i] >= 48 && hex[i] <= 57)	//Number ASCII
			nibble = hex[i] - 48;
		else if (hex[i] >= 65 && hex[i] <= 70)	//Uppercase ASCII
			nibble = hex[i] - 55;
		else if (hex[i] >= 97 && hex[i] <= 102)	//Lowercase ASCII
			nibble = hex[i] - 87;
		else
			return -1;		//Illegal character in our hex value

		retVal = (retVal << 4) | nibble;
	}

	return retVal;
}